using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Diagnostics;

namespace Slobby.Client.Net {
    public class ServerConnection {
        static Slobby.Common.Dispatcher<PassthroughReciever> dispatcher =
            new Slobby.Common.Dispatcher<PassthroughReciever>();

        static Slobby.Common.Dispatcher<PreAuthReciever> preauthdispatch =
            new Slobby.Common.Dispatcher<PreAuthReciever>();

        private Connection connection;
        private PassthroughReciever reciever;
        private PreAuthReciever preauthreciever;

        private volatile bool disconectserved = true;

        private bool authed = false;

        public event Action<string> AuthenticationFailure;
        public event EventHandler Authenticated;
        public event Action<string> Disconnected;
        public event Action<object> MessageRecieved;

        public ServerConnection() {
            reciever = new PassthroughReciever(this, RaiseMessage, DisconRecieve);
        }

        public void Connect(string address, int port, string userName, string credentials) {
            connection = new Connection(address, port);

            connection.Disconnected += new Action<string>(connection_Disconnected);
            connection.FailedConnect += new Action<string>(connection_FailedConnect);
            connection.Connected += new EventHandler(connection_Connected);
            connection.MessageRecieved += new Action<object>(connection_MessageRecieved);


            preauthreciever = new PreAuthReciever(this);

            connection.Connect();

            Version assemblyVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
            SendMessage(new Slobby.SharedNetworkTypes.VersionMessage(assemblyVersion));
            SendMessage(new Slobby.SharedNetworkTypes.TryAuthenticationMessage(userName, credentials));
        }

        void connection_MessageRecieved(object obj) {
            dispatcher.Dispatch(reciever, obj);
        }

        void connection_Connected(object sender, EventArgs e) {
            disconectserved = false;
        }

        void connection_FailedConnect(string obj) {
            connection.Disconnect();
            if (AuthenticationFailure != null)
                AuthenticationFailure(obj);
        }

        void connection_Disconnected(string obj) {

            RaiseDisconnect(obj);

        }

        public void SendMessage(object message) {
            connection.SendMessage(message);
        }

        public void RaiseMessage(object o) {
            if (!authed) {
                preauthdispatch.Dispatch(preauthreciever, o);
            }
            else {
                MessageRecieved(o);
            }
        }

        public void Disconnect() {
            Disconnect("quit");
        }

        /// <summary>
        /// Forces the connection to send all remaining messages then terminate in a non-blocking fashion
        /// </summary>
        /// <param name="message">Message to be sent to the server, usually the reason for the disconnect</param>
        public void Disconnect(string message) {
            SendMessage(new Slobby.SharedNetworkTypes.DisconnectedMessage(message));
            connection.Disconnect();
        }

        private void DisconRecieve(Slobby.SharedNetworkTypes.DisconnectedMessage discon) {
            RaiseDisconnect(discon.Reason);
            connection.Disconnect();
        }

        private void RaiseDisconnect(string message) {
            if (!disconectserved) {
                disconectserved = true;
                if (authed) {
                    
                    if (Disconnected != null)
                        Disconnected(message);
                }
                else {
                    if (AuthenticationFailure != null)
                        AuthenticationFailure(message);
                }
            }
        }

        public void FailAuth(string reason) {
            if (AuthenticationFailure != null)
                AuthenticationFailure(reason);
        }

        public void PassAuth() {
            authed = true;
            if (Authenticated != null)
                Authenticated(this, new EventArgs());
        }
    }
}
